package com.chuiyuan.utils;

import java.util.LinkedList;
import java.util.List;

/**
 * Created by chuiyuan on 2/21/16.
 * Main:
 * WorkThread [] workThreads
 * List<Runnable> taskQueue
 */
public final class ThreadPool {
    //default 5 thread
    private static int worker_num =5;
    //worker thread
    private WorkThread [] workThreads;
    //tasks done
    private static volatile int finished_task=0;
    //task queue, as a buffer, List not thread safe
    private List<Runnable> taskQueue = new LinkedList<Runnable>() ;
    private static ThreadPool threadPool ;

    private ThreadPool(){
        this(5);
    }

    private ThreadPool(int worker_num){
        this.worker_num = worker_num;
        workThreads = new WorkThread[worker_num];
        for (int i=0;i<worker_num;i++){
            workThreads[i] = new WorkThread();
            workThreads[i].start();//start thread in pool
        }
    }
    /**
     * singleton
     */
    public static ThreadPool getThreadPool(){
        return getThreadPool(worker_num);
    }

    public static ThreadPool getThreadPool(int worker_num1){
        if (worker_num1<=0){
            worker_num1 = ThreadPool.worker_num;
        }
        if (threadPool ==null){
            threadPool = new ThreadPool(worker_num1);
        }
        return threadPool;
    }

    /**
     * Just add task to TaskQueue, when to start task is
     * decided by ThreadPool
     * @param task
     */
    public void execute(Runnable task){
        synchronized (taskQueue){
            taskQueue.add(task);
            taskQueue.notify();
        }
    }

    /**
     * Add task to TaskQueue in batch
     * @param tasks
     */
    public void execute(Runnable [] tasks){
        synchronized (taskQueue){
            for (Runnable task :tasks){
                taskQueue.add(task);
            }
            taskQueue.notify();
        }
    }

    /**
     * Add task in List
     * @param tasks
     */
    public void execute(List<Runnable> tasks){
        synchronized (taskQueue){
            for (Runnable task: tasks){
                taskQueue.add(task);
            }
            taskQueue.notify();
        }
    }

    /**
     * shutdown all the thread if all tasks are done,if not,
     * wait till all done
     */
    public void shutdown(){
        //if not done ,sleep for a while??
        while (!taskQueue.isEmpty()){
            try {
                System.out.println("Thread "+Thread.currentThread().getName()+"" +
                        " want to shutdown ThreadPool");
                Thread.sleep(10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        for (int i=0;i<worker_num;i++){
            workThreads[i].stopWorker();
            workThreads[i] = null;
        }
        threadPool = null ;
        taskQueue.clear();//clear taskQueue
    }

    public int getWorkThreadNumber(){
        return worker_num;
    }

    /**
     * tasks pop out of TaskQueue, it may haven't
     * done
     * @return
     */
    public int getFinishedTaskNumber(){
        return  finished_task;
    }

    /**
     * tasks left in TaskQueue
     * @return
     */
    public int getWaitTaskNumber(){
        return taskQueue.size();
    }

    @Override
    public String toString(){
        return "WorkThreadNumber:"+getWorkThreadNumber()+",FinishedTaskNumber"+
                getFinishedTaskNumber()+",WaitTaskNumber:"+getWaitTaskNumber();
    }

    private class WorkThread extends Thread{
        //use to stop thread
        private boolean isRunning = true ;
        /**
         * key in run()
         * if TaskQueue is not null, get task and run.
         * if TaskQueue is null, wait
         */
        @Override
        public void run(){
            Runnable r = null;
            //inner class has a reference of outclass.this
            //so it can read outclass.this.taskQueue
            while(isRunning){
                synchronized (taskQueue){
                    while(isRunning&& taskQueue.isEmpty()){
                        try {
                            taskQueue.wait(10);
                        }catch (InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    if (!taskQueue.isEmpty()){
                        r = taskQueue.remove(0);//get out task
                    }
                }
                if (r!=null){
                    r.run();//run task in this thread
                }
                finished_task++;
                r = null ;
            }
        }

        public void stopWorker(){
            isRunning = false;
        }
    }
}
